﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;


public class MyUdpServerConn
{

    private const bool Log_Enable = true;

    private readonly string m_Name;
    private readonly Queue<SocketHelper.ThreadMessage> m_ThreadMessageQueue;
    public int m_BindPort = 8081;

    private Socket m_Socket;
    private bool m_IsStopping;
    
    private Thread m_LoopThread;

    private bool m_BeginRecvRunning;
    private byte[] m_RecvBuff = new byte[UdpHelper.UDP_Slice_Head_Len + UdpHelper.UDP_Slice_Max_Body_Size];
    private int m_NotReadLen;

    private EndPoint m_TempEP = new IPEndPoint(IPAddress.Any, 0);

    private Dictionary<EndPoint, MyUdpServerClientConn> m_ClientDict = new Dictionary<EndPoint, MyUdpServerClientConn>();
    private List<MyUdpServerClientConn> m_ClientList = new List<MyUdpServerClientConn>();


    public MyUdpServerConn(string name, Queue<SocketHelper.ThreadMessage> msgQueue)
    {
        m_Name = name;
        m_ThreadMessageQueue = msgQueue;
    }

    public void Start()
    {
        if (null != m_Socket) return; //Stop没调用
        if (null != m_LoopThread || m_BeginRecvRunning) return; //Stop调用了, 还在stopping中

        if (m_IsStopping)
        {
            m_IsStopping = false;

            m_BeginRecvRunning = false;
        }

        m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        var bindEP = new IPEndPoint(IPAddress.Parse("127.0.0.1"), m_BindPort); //发送和接收, 都走该端口
        m_Socket.Bind(bindEP);
        //m_Socket.NoDelay = true;

        m_LoopThread = new Thread(OnLoopThread);
        m_LoopThread.IsBackground = true;
        m_LoopThread.Start();

        m_BeginRecvRunning = true;
        NextRecvCycle(m_Socket);
    }

    public int Status
    {
        get
        {
            if (null != m_Socket)
                return 1; //started

            if (null != m_LoopThread || m_BeginRecvRunning)
                return -1; //stopping

            return 0; //stopped
        }
    }


    //没Started, 没Stopped, 则Stopping中
    public bool IsStarted
    {
        get { return null != m_Socket; }
    }

    public bool IsStopped
    {
        get { return (null == m_LoopThread && !m_BeginRecvRunning); }
    }

    public void Stop()
    {
        if (null == m_Socket) return;
        if (m_IsStopping) return;
        m_IsStopping = true;

        var t = m_LoopThread;
        if (null != t)
            t.Interrupt();

        if (m_BeginRecvRunning) //否则, 等退出的时候Clear
        {
            m_ClientDict.Clear();
        }

        SocketHelper.ShutdownAndClose(m_Socket);
        m_Socket = null;
    }

    private void OnLoopThread(object obj)
    {
        while (!m_IsStopping)
        {
            //检查超时重发
            //检查Ping
            try
            {
                lock (m_ClientList)
                {
                    foreach (var c in  m_ClientList)
                    {
                        c.CheckReSend();
                        //c.CheckPing();
                    }
                }

                Thread.Sleep(5);
            }
            catch (ThreadInterruptedException ex)
            {
                Debug.LogWarning($"{ex.Message}");
                break;
            }
            catch (Exception ex)
            {
                Debug.LogError($"{ex.GetType().Name}:{ex.Message}");
            }
        }

        lock (m_ClientList)
        {
            foreach (var c in m_ClientList)
            {
                //c.Stop();
            }
        }

        m_LoopThread = null;
        Debug.Log($"{m_Name}: LoopThread exit: tid:{Thread.CurrentThread.ManagedThreadId}");
    }

    //******************** 接收

    private void OnBeginReceiveResult(IAsyncResult ar)
    {
        var socket = (Socket)ar.AsyncState;

        long now = SocketHelper.CurrentTimeMillis();
        try
        {
            int recvLen = socket.EndReceiveFrom(ar, ref m_TempEP);
            if (Log_Enable) Debug.Log($"{m_Name}: recv data: len:{recvLen}, notReadLen:{m_NotReadLen} <-{m_TempEP}");

            if (!m_ClientDict.TryGetValue(m_TempEP, out var client))
            {
                client = new MyUdpServerClientConn(this, socket, m_TempEP);
                m_ClientDict.Add(m_TempEP, client);
                lock (m_ClientList)
                {
                    m_ClientList.Add(client);
                }
            }

            client.m_LastRecvTime = now;
            client.m_PingNum = 0;

            if (recvLen <= 0) //FIN
            {
                //todo: 中断连接?
                if (Log_Enable) Debug.Log($"{m_Name}: FIN: from:{m_TempEP}");
            }
            else
            {
                m_NotReadLen += recvLen;

                int readedLen = 0;
                while (UdpHelper.IsUdpSliceReady(m_RecvBuff, readedLen, m_NotReadLen, out var opCode, out var bodyLen))
                {
                    client.ParseRecvData(socket, m_RecvBuff, readedLen, opCode, bodyLen);

                    m_NotReadLen -= UdpHelper.UDP_Slice_Head_Len;
                    m_NotReadLen -= bodyLen;

                    readedLen += UdpHelper.UDP_Slice_Head_Len;
                    readedLen += bodyLen;
                }

                if (m_NotReadLen > 0) //udp没有粘包问题
                {
                    Debug.LogError($"{m_Name}: RecvThread: readedLen:{readedLen}, notReadLen:{m_NotReadLen}, local:{socket.LocalEndPoint}");
                    //Buffer.BlockCopy(m_RecvBuff, readedLen, m_RecvBuff, 0, m_NotReadLen);
                }

                if (!m_IsStopping)
                {
                    if (Log_Enable) Debug.Log($"{m_Name}: nextRecvCycle: tid:{Thread.CurrentThread.ManagedThreadId}");
                    NextRecvCycle(socket);
                    return;
                }
            }
        }
        catch (ObjectDisposedException ex) //shutdown或close
        {
            Debug.LogWarning($"{m_Name}: {ex.Message}");
        }
        catch (Exception ex)
        {
            Debug.LogError($"{m_Name}: RecvThread: connected:{socket.Connected}, {ex.GetType().Name}: {ex.Message}");
            Debug.LogError($"{m_Name}: {ex.StackTrace}");
        }

        if (m_IsStopping)
        {
            m_ClientDict.Clear();
        }
        m_BeginRecvRunning = false; //没执行到RecvCycle就退出了
    }

    private void NextRecvCycle(Socket socket)
    {
        try
        {
            m_NotReadLen = 0;
            int buffRemainLen = m_RecvBuff.Length - m_NotReadLen;
            socket.BeginReceiveFrom(m_RecvBuff, m_NotReadLen, buffRemainLen, 0, ref m_TempEP, OnBeginReceiveResult, socket);
        }
        catch (Exception ex)
        {
            if (Log_Enable) Debug.LogError($"{m_Name}: NextRecv: connected:{socket.Connected}, {ex.GetType().Name}, {ex.Message}");
        }
    }

    //********************

    //********************

    public void EnqueueMessage(SocketHelper.ThreadMessage msg)
    {
        if (m_IsStopping) //渲染线程正在写true前, 这边读到了false: 则渲染线程后续会读到失效连接的消息, 要紧吗?
        {
            Debug.LogWarning($"{m_Name}: have close: msg discard:{msg.m_Cmd}");
            return;
        }

        lock (m_ThreadMessageQueue)
        {
            m_ThreadMessageQueue.Enqueue(msg);
        }
    }

}
